package com.wf.articleManagement.service.local.impl;


import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.wf.apicommons.config.RabbitConstant;
import com.wf.apicommons.entity.articleManagement.ArticleDetails;
import com.wf.apicommons.entity.MessageBoard.MqMessage;
import com.wf.apicommons.utils.*;
import com.wf.articleManagement.es.dao.BlogRepository;
import com.wf.articleManagement.mapper.ArticleDetailsMapper;
import com.wf.articleManagement.service.call.MessageBoardFeignService;
import com.wf.articleManagement.service.local.BlogRelatedService;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;



/**
 * @Description: 博客Service实现类
 */
@Slf4j
@Service
public class BlogRelatedServiceImpl implements BlogRelatedService {
    //es索引名称
    private final  static  String INDEX_NAME="article-management";

    @Autowired
    private  ArticleDetailsMapper adm;
    @Autowired
    private BlogRepository br;

    @Autowired
    private RestHighLevelClient client;

    @Resource
    MessageBoardFeignService mbfs;

    /**
     * 发布文章
     * @param articleDetails
     * @return
     */
    @Override
    public CommonResult<String> createArticle(ArticleDetails articleDetails) {
        if(StrUtil.isNotBlank(articleDetails.getTitle())&&StrUtil.isNotBlank(articleDetails.getContent())&&articleDetails.getStatus()!=null){
            articleDetails.setID(SnowflakeUtil.getID());

            if(adm.insertBlogDetails(articleDetails)>0&&articleDetails.getStatus()==1){
                //将数据保存到ES
               // br.save(articleDetails);
                //将Es数据推送到MQ，在消费者处进行刷新数据
                mbfs.pushBlogData(MqMessage.setMsgData(articleDetails,MqMessage.CREATE_OR_UPDATE));
            }

            return CommonResult.success("发布成功");
        }
        return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);
    }


    /**
     * 上传文件到COS
     * @param file 要上传的文件
     * @param remarks 文件备注
     * @return
     */
    @Override
    public CommonResult<String> fileUploadCos(MultipartFile file,String remarks) {
        if(file!=null&&StrUtil.isNotBlank(remarks)){
            //上传文件
            String fileUrl = COSFileUtil.fileUpload(file, remarks);
            if(fileUrl!=null){
                return CommonResult.success(fileUrl);
            }
            return CommonResult.fail(CodeEnum.FILE_UPLOAD_FAILED);
        }
        return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);
    }

    /**
     * 查询文章列表
     * @param param
     * @return
     */
    @Override
    public CommonResult<Map<String,Object>> queryArticleList(Map<String, Object> param) {
        int page=MapUtil.getInt(param,"page",0);
        int size=MapUtil.getInt(param,"size",10);
        String title=MapUtil.getStr(param,"title");
        if(page>0&&size>0){
            //获取es搜索结果
            Map<String,Object> blogList = highLightSearchBlog(INDEX_NAME, "title", "content", title, null,page, size);
            return CommonResult.success(blogList);
        }

        return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);
    }




    /**
     * 高亮搜索+分页+按照指定字段排序
     *
     * @param indexName 索引名称
     * @param field 需要匹配的字段
     * @param field2 需要匹配的字段
     * @param keyword 匹配的值
     * @param userId 需要匹配的用户ID值
     * @param pageNum 分页页码
     * @param pageSize 单页展示条数
     * @return 返回替换原本内容后的数据
     */
    @Override
    public Map<String,Object> highLightSearchBlog(String indexName, String field, String field2, String keyword, String userId,Integer pageNum, Integer pageSize) {
        Map<String,Object> resultMap=new HashMap<>();
         //返回结果
        List<Map<String, Object>> resList = new ArrayList<>();
        resultMap.put("searchList",resList);
        resultMap.put("totalNumber",0);
        resultMap.put("pageCount",0);
        // 指定索引名称，名称可以指定多个
        SearchRequest searchRequest = new SearchRequest(indexName);
        //创建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //判断是否需要匹配用户ID
        if(StrUtil.isNotBlank(userId)){
            QueryBuilder orderQuery = QueryBuilders.nestedQuery("userInformationId",
                    QueryBuilders.boolQuery()
                            .filter(QueryBuilders.matchQuery("userInformationId.ID", userId)),
                    ScoreMode.None);

            QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(orderQuery);
            searchSourceBuilder.postFilter(queryBuilder);
        }
        //避免匹配空字符串
        if("".equals(keyword)){
            //设置需要匹配的字段及匹配的值
            searchSourceBuilder.query(QueryBuilders.matchAllQuery());
            //按照时间倒序排序
            searchSourceBuilder.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));
        }else{
            //设置需要匹配的字段及匹配的值
            searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keyword, field, field2));
            //按照score,即匹配度，倒序排序
            searchSourceBuilder.sort(SortBuilders.scoreSort().order(SortOrder.DESC));
        }

        // 设置分页参数
        searchSourceBuilder.from((pageNum - 1) * pageSize);
        searchSourceBuilder.size(pageSize);
        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        // 设置高亮规则
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field(field).field(field2);
        //设置高亮代码
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);
        // SearchSourceBuilder 添加到 SearchRequest
        searchRequest.source(searchSourceBuilder);
        try {
            // 搜索
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            // 查询到的文档
            SearchHit[] hits = response.getHits().getHits();
            //获取总记录条数
            TotalHits totalHits = response.getHits().getTotalHits();
            resultMap.put("totalNumber",totalHits.value);
            resultMap.put("pageCount",(totalHits.value+pageSize-1) / pageSize);


            for (SearchHit hit : hits) {

                // 获取到全部字段高亮部分
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();

                // 用高亮后的结果取代原来字段
                Map<String, Object> source = hit.getSourceAsMap();

                if (highlightFields.get(field) != null) {
                    // 获取 指定field部分高亮出的片段
                    Text[] fragments = highlightFields.get(field).fragments();
                    // 拼装
                    StringBuilder builder = new StringBuilder();
                    for (Text text : fragments) {
                        builder.append(text);
                    }

                    source.put(field, builder.toString());
                }

                if (highlightFields.get(field2) != null) {
                    // 获取 指定field部分高亮出的片段
                    Text[] fragments2 = highlightFields.get(field2).fragments();
                    // 拼装
                    StringBuilder builder2 = new StringBuilder();
                    for (Text text2 : fragments2) {
                        builder2.append(text2);
                    }
                    source.put(field2, builder2.toString());
                }
                // 将这条记录加入结果集
                resList.add(source);
            }
        } catch (IOException e) {
            e.printStackTrace();

        }
        return resultMap;
    }


    /**
     * 查询指定文章详情
     * @param articleId 文章ID
     * @return
     */
    @Override
   public  CommonResult<Map<String,Object> > queryBlogDetails(String articleId,boolean isCompile){
        if(StrUtil.isNotBlank(articleId)){
            Map<String, Object> blogDetails = adm.queryBlogDetails(articleId);
            String content=MapUtil.getStr(blogDetails,"content");
            if(isCompile&&blogDetails!=null&&StrUtil.isNotBlank(content)){
                blogDetails.put("content",MarkdownToHtmlUtils.markdownToHtml(content));
            }

            //查询指定文章详情
            return CommonResult.success(blogDetails);
        }
        return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);
    }

    /**
     * 查询指定用户的文章列表
     * @param param
     * @return
     */
    @Override
    public CommonResult<Map<String, Object>> querySpecifyUserArticleList(Map<String, Object> param) {
        String userId=MapUtil.getStr(param,"userId");
        String searchContent=MapUtil.getStr(param,"searchContent");
        int page=MapUtil.getInt(param,"page",0);
        int size=MapUtil.getInt(param,"size",10);

        if(StrUtil.isNotBlank(userId)&&page>0&&size>0){
            //获取指定用户的博客列表
            Map<String,Object> blogList = highLightSearchBlog(INDEX_NAME, "title", "content", searchContent, userId,page, size);
            return CommonResult.success(blogList);
        }
        return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);
    }

    /**
     * 删除文章
     * @param articleId
     * @return
     */
    @Override
    public CommonResult<String> deleteArticle(String  articleId) {
        if(StrUtil.isNotBlank(articleId)){
            //删除mysql数据
            if(adm.deleteById(articleId)>0 ){
                //删除es数据
                //  br.deleteById(articleId);
                ArticleDetails articleDetails=new ArticleDetails();
                articleDetails.setID(articleId);
                //将Es数据推送到MQ，在消费者处进行刷新数据
                CommonResult<String> result = mbfs.pushBlogData(MqMessage.setMsgData(articleDetails, MqMessage.DELETE));
                if(result!=null&&result.getCode()==CodeEnum.SUCCESS.getCode()){
                    return  CommonResult.success("删除成功");
                }
            }
                return  CommonResult.fail(CodeEnum.ERROR);


        }
        return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);
    }

    /**
     * 修改文章
     * @param articleDetails
     * @return
     */
    @Override
    public CommonResult<String> updateArticle(ArticleDetails  articleDetails) {
        if(StrUtil.isNotBlank(articleDetails.getID())&&StrUtil.isNotBlank(articleDetails.getTitle())&&StrUtil.isNotBlank(articleDetails.getContent())&&articleDetails.getStatus()!=null&&articleDetails.getCreateTime()!=null&&articleDetails.getUserInformationId()!=null&StrUtil.isNotBlank(articleDetails.getUserInformationId().getID())){
            articleDetails.setUpdateTime(new Date());
            //修改mysql数据
            if(adm.updateBlogDetails(articleDetails)>0){
                //修改es数据
             //   br.save(articleDetails);
                //将Es数据推送到MQ，在消费者处进行刷新数据
                mbfs.pushBlogData(MqMessage.setMsgData(articleDetails,MqMessage.CREATE_OR_UPDATE));
                return  CommonResult.success("修改成功");
            }else{
                return  CommonResult.fail(CodeEnum.ERROR);
            }

        }
        return CommonResult.fail(CodeEnum.ILLEGAL_PARAMETER);
    }


    /**
     * 监听推送的博客数据
     * @param blogMsg
     */
    @RabbitListener(queues = RabbitConstant.ES_QUEUE)
    public void process(String blogMsg) {
        MqMessage<ArticleDetails> mqMessage = JSONUtil.toBean(blogMsg, MqMessage.class);
        //刷新ES数据
        flushESData(mqMessage);
    }


    /**
     * 刷新ES数据
     * @param flushData
     */
    @Override
    public void flushESData(MqMessage<ArticleDetails> flushData){
        ArticleDetails articleDetails=JSONUtil.toBean(flushData.getJsonData(),ArticleDetails.class);
        //判断当前的操作类型
        switch (flushData.getOperation()) {
            case MqMessage.CREATE_OR_UPDATE:
               br.save(articleDetails);
                break;
            case MqMessage.DELETE:
                br.deleteById(articleDetails.getID());
                break;
            default:
                log.error("在{}提示，操作类型错误，刷新ES数据失败,消息:{}", this.getClass().getName(),flushData);
                break;
        }
    }


}
